import tester.*;
import tester.cobertura.Instrument;

// a method to verify equality of this item and the given one
@Instrument
interface IsSame<T>{
  // is this data the same as the given data?
  boolean isSame(T t);
}

// a method to covert types to a string representation.
@Instrument
interface MakeString<T>{
  // Calls the makeString in the specific Object.
  String makeString();
}

//a method to decide whether the given item
//has the desired property
@Instrument
interface ISelector<T>{
  //does the given item have the desired property?
  boolean pick(T t);
}

// a method to compute the value this item 
@Instrument
interface IValue<T>{
  // compute the value of this data
  int value();
}

// ...
@Instrument
interface IFilter2<T>{
  ILo<T> filter2(ISelector<T> s);
}

//a method to decide whether the given book
//costs less than $20
@Instrument
class CheapBook implements ISelector<Book>{
  //does the given book cost less than $20?
  public boolean pick(Book b){
    return b.price < 20;
  }
}

//a method to decide whether the given book
//costs less than $20
@Instrument
class LongSong implements ISelector<Song>{
  //does the given book cost less than $20?
  public boolean pick(Song s){
    return s.duration > 180;
  }
}

//a method to decide whether the given book
//costs less than $20
@Instrument
class JPegImage implements ISelector<Image>{
  //does the given book cost less than $20?
  public boolean pick(Image i){
    return i.fileType.equals("jpeg");
  }
}

// to represent a list of items of the type <T>
@Instrument
interface ILo<T>{
  
  // compute the total size of this list
  int size();
  
  // compute the total value of this list
  int totalValue();
  
  // does this list contain the given item?
  boolean contains(T t);
  
  // represents the given list as a string.
  public String makeString();
  
  // filter
  public ILo<T> filter2(ISelector<T> s);
}                                

// to represent an empty list of books
@Instrument
class MtLo<T> implements ILo<T>{
  MtLo(){}
  
  // compute the total size of this list
  public int size(){
    return 0;
  }
  
  // compute the total value of this list
  public int totalValue(){
    return 0;
  }
  
  // does this list of books contain the given book?
  public boolean contains(T b){
    return false;
  }
  
  // represents the given list as a string.
  public String makeString(){
    return "";
  }
  
  // filter
  public ILo<T> filter2(ISelector<T> s){
    return this;
  }
}                                              

// to represent a nonempty list of books
@Instrument
class ConsLo<T> implements ILo<T>{
  T first;
  ILo<T> rest;
  
  ConsLo(T first, ILo<T> rest){
    this.first = first;
    this.rest = rest;
  }
  
  
  /* TEMPLATE:
   FIELDS:
    ... this.first ...              -- <T>
    ... this.rest ...               -- ILo<T>

    METHODS FOR FIELDS:
    ... this.rest.size() ...         -- int
    ... this.rest.contains(T) ...    -- boolean

   */
  
  // compute the total size of this list
  public int size(){
    return 1 + this.rest.size();
  }
  
  // compute the total value of this list
  public int totalValue(){
    return ((IValue<T>)this.first).value() + this.rest.totalValue();
  }
  
  // does this list contain the given item?
  public boolean contains(T t){
    return ((IsSame<T>)this.first).isSame(t) ||
    this.rest.contains(t);
  }
  
  // represents the given list as a string.
  public String makeString(){
    return ((MakeString<T>)this.first).makeString() + ", " + this.rest.makeString();
  }
  
  // filter
  public ILo<T> filter2(ISelector<T> s){
    if(s.pick(this.first)){
      return new ConsLo(this.first, this.rest.filter2(s));
    } else {
      return this.rest.filter2(s);
    }
  }
}                                                    

